package com.gusheng.mybatisplug.handler;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.gusheng.mybatisplug.annotation.DataAuth;
import com.gusheng.mybatisplug.auth.IDataAuth;
import com.gusheng.mybatisplug.exceptions.DataAuthNotFountBeanExceptions;

/**
 * 	控制类<br>
 * 	保存IDataAuth的实力<br>
 * 	对任务进行判断-分派<br>
 * 
 * @author Administrator
 *
 */
public class DataAuthHandler {
	
	// 保存自定义权限拦截
	private Map<String,IDataAuth> dataAuthMap;
	
	// 自定义权限缓存,缓存 queryId 是否需要权限控制,由哪个实现来控制
	private Map<String,QueryId> queryIdCache = new HashMap<>();
	
	// 用以判断是否嵌套拦截
	static ThreadLocal<CallStatus> localVar = new ThreadLocal<>();
	
	/**
	 * 	初始化<br>
	 * 	因为如果在IDataAuth中注入Mybatis-Dao层的实例<br>
	 * 	可能会出现Bean加载顺序的问题<br>
	 * 	因此会在所有Bean加载完成后调用init方法进行初始化<br>
	 * @param list IDataAuth实例
	 */
	public void init ( List<IDataAuth> list ) {
		
		if ( list != null ) {
			this.dataAuthMap = new HashMap<>( list.size() *2 );
			
			list.forEach( item -> dataAuthMap.put( item.getAuthName() , item) );
		}
	}

	/**
	 * 	初始化<br>
	 * 	因为如果在IDataAuth中注入Mybatis-Dao层的实例<br>
	 * 	可能会出现Bean加载顺序的问题<br>
	 * 	因此会在所有Bean加载完成后调用init方法进行初始化<br>
	 * @param dataAuthMap ( key = 拦截器名称 , value = IDataAuth实例 )
	 */
	public void init ( Map<String,IDataAuth> dataAuthMap ) {
		this.dataAuthMap = dataAuthMap;
	}
	
	/**
	 * 	开始对查询进行判断-分派<br>
	 * 	1. 先从缓存的确认此查询ID判断是否需要拦截<br>
	 * 	未命中缓存或缓存结果表示需要拦截的时候,进行下一步
	 * 	2.判断拦截器是否执行过
	 * @param targetSql
	 * @param queryId
	 * @return
	 */
	public String executeAuth( String targetSql , String queryId ) {

		// 1.获取缓存
		QueryId queryIds = findQueryId( queryId );
		
		if ( !queryIds.getIsAuth() ) {
			return targetSql;
		}
		
		/* 2.判断拦截器是否执行过
		 * 	只有未执行的状态才进行拦截处理
		 */
		String authName = queryIds.getAuthName();
		
		// 判断本次调用是否是起点,如果是起点调用完成后清理ThreadLocal
		boolean isOrigin = false;
		
		CallStatus callStatus = localVar.get();
		
		if ( callStatus == null ) {
			callStatus = new CallStatus();
			localVar.set( callStatus );
			isOrigin = true;
		}
		
		Integer status = callStatus.getStatusByName(authName);

		// 如果不是未调用状态,则不进行拦截(只调用一次)
		if ( status.equals( 0 ) ) {
			
			IDataAuth dataAuth = queryIds.getiDataAuth();
			
			targetSql = dataAuth.auth(targetSql);
			callStatus.upStatus(authName, CallStatus.STATUS_CALL_END);
		}
		
		if ( isOrigin )  localVar.remove();
		
		return targetSql;
	}
	
	/**
	 * 	根据查询ID找到缓存<br>
	 * 	如果有找到,则创建
	 * @param queryId
	 * @return
	 */
	public QueryId findQueryId( String queryId ) {
		
		/*
		 * 	 命中缓存,直接返回
		 */
		QueryId result = queryIdCache.get( queryId );
		if ( result != null ) return result;
		
		/*
		 *	否则将queryId视作mapper方法的全路径名称来处理
		 * 	确认方法是否包含@AuthData注释
		 */
		result = new QueryId(false);

		int slipt = queryId.lastIndexOf(".");
		
		String className = queryId.substring(0, slipt);
		String methodName = queryId.substring( slipt + 1 );
		Class<?> clazz = null;
		try {
			
			clazz = Class.forName( className );
			
		} catch (ClassNotFoundException e) {
			
			// 根据 queryId 找不到class,加入false
			queryIdCache.put( queryId, result );
			e.printStackTrace(); 
			return result;
			
		}
		
		// 找到方法中的@DataAuth,取出authName
		Method[] methods = clazz.getMethods();
		
		String authName = null;
		
		for ( Method method : methods ) 
			if ( methodName.equals( method.getName() ) && method.isAnnotationPresent(DataAuth.class) ) {
				DataAuth dataAuth = method.getAnnotation(DataAuth.class);
				authName = dataAuth.value();
				break;
			}
		
		// 设置是否进行数据权限拦截
		if ( authName != null ) {
			
			// 设置数据拦截器名称
			result.setAuthName( authName );
			// 添加实现
			IDataAuth dataAuth = dataAuthMap.get( authName );
			
			if ( dataAuth == null ) 
				throw new DataAuthNotFountBeanExceptions();
			
			result.setiDataAuth( dataAuth );
			
			result.setIsAuth( true );
		} 
		
		
		// 加入缓存
		queryIdCache.put(queryId, result);
		return result;
	}
	
}
